package GtEngine

import (
	"net"
	"strconv"
	"sync"
	"time"
)

import (
	. "tksLog"
	. "tksStatic"
	// . "tksTcp"
	"GtDB"
	. "tksUint"
)

type ClientInfo struct {
	Id           uint32
	ServerID     uint32
	TimeoutCount int
	Conn         net.Conn
	t            *time.Ticker
	Toc          chan int
	//
	Info TokenInfo
}

var clientID uint32 = 0
var m *sync.Mutex

var Clientsmutex *sync.Mutex
var IdCLients map[uint32]*ClientInfo
var AddrCLients map[string]*ClientInfo

func init() {
	m = new(sync.Mutex)
	Clientsmutex = new(sync.Mutex)
	IdCLients = make(map[uint32]*ClientInfo)
	AddrCLients = make(map[string]*ClientInfo)
}

func (c *ClientInfo) run() {
	for {
		select {
		case state := <-c.Toc:
			c.TimeoutCount = state
		case <-c.t.C:
			if c.TimeoutCount > 3 {
				Clientsmutex.Lock()
				delete(IdCLients, c.Id)
				delete(AddrCLients, c.Conn.RemoteAddr().String())
				c.Conn.Close()
				Clientsmutex.Unlock()
				//
				GateSubClient()
				//
				Debug("client[%d] %s timeout", c.Id, c.Conn.RemoteAddr().String())
				return
			} else if c.TimeoutCount >= 0 {
				c.TimeoutCount = c.TimeoutCount + 1
			} else {
				break
			}
		}
	}
}

func FindInfoByToken(token string, info *TokenInfo) error {
	err := GtDB.Cache.Hget(token, info)
	if err != nil {
		return err
	}
	GtDB.Cache.Hdel(token, info)
	return nil
}

func getNewGateClientId() uint32 {
	m.Lock()
	defer m.Unlock()
	clientID += 1
	//高8位Gate内部id（对外通讯gate的id为0），低24位为一个唯一id
	clientID = (GateInfo.GateId << 24) | ((clientID & 0xFFFFFF) + 1)
	return clientID
}

func NewGateClient(conn net.Conn, serverID uint32, ti *TokenInfo) {
	if FindGateClientByAddr(conn.RemoteAddr().String()) != nil {
		return
	}
	info := new(ClientInfo)
	info.Id = getNewGateClientId()
	info.ServerID = serverID
	info.TimeoutCount = 0
	info.Conn = conn
	info.Toc = make(chan int, 1)
	info.t = time.NewTicker(5 * time.Second)
	info.Info = *ti
	go info.run()
	//
	Clientsmutex.Lock()
	IdCLients[info.Id] = info
	AddrCLients[conn.RemoteAddr().String()] = info
	//
	GtDB.Cache.Hset(strconv.FormatUint(uint64(info.Id), 10), ti)

	Debug("==========%d, %d", len(IdCLients), len(AddrCLients))
	Clientsmutex.Unlock()
	//
	GateAddClient()
}

func FindGateClientById(id uint32) *ClientInfo {
	Clientsmutex.Lock()
	defer Clientsmutex.Unlock()
	info, ok := IdCLients[id]
	if ok {
		return info
	} else {
		return nil
	}
}

func FindGateClientByAddr(addr string) *ClientInfo {
	Clientsmutex.Lock()
	defer Clientsmutex.Unlock()
	info, ok := AddrCLients[addr]
	if ok {
		return info
	} else {
		return nil
	}
}

func ClientConnect(conn net.Conn, from uint32, to uint32, buf []byte) {
	json, err := BufToMsg(buf)
	if err != nil {
		sendRsp(conn, CmdClientConnectGateRsp, to, from, RetMsgFormatError)
		return
	}
	serverId, _ := json.Get("ServerId").Int()
	token, _ := json.Get("Token").String()
	//
	var info TokenInfo
	if FindInfoByToken(token, &info) != nil {
		sendRsp(conn, CmdClientConnectGateRsp, to, from, RetTokenError)
		return
	}
	//
	if FindGateClientByAddr(conn.RemoteAddr().String()) != nil {
		sendRsp(conn, CmdClientConnectGateRsp, to, from, RetFail)
		return
	}
	NewGateClient(conn, uint32(serverId), &info)
	Debug("recv from client: %s, connect server: %d, token: %s", conn.RemoteAddr().String(), serverId, token)
	sendRsp(conn, CmdClientConnectGateRsp, to, from, RetSucc)
}
